1   /*
2    * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package sun.awt.image;
27  import java.awt.image.Raster;
28  import java.awt.image.WritableRaster;
29  import java.awt.image.RasterFormatException;
30  import java.awt.image.SampleModel;
31  import java.awt.image.BandedSampleModel;
32  import java.awt.image.DataBuffer;
33  import java.awt.image.DataBufferByte;
34  import java.awt.Rectangle;
35  import java.awt.Point;
36  
37  /**
38   * This class defines a Raster with pixels consisting of multiple
39   * 8-bit samples stored in possibly separate arrays for each band.
40   * Operations on sets of pixels are performed on a given band in the
41   * Raster before moving on to the next band.  The arrays used for
42   * storage may be distinct or shared between some or all of the bands.
43   * Each band additionally has an offset that is added to determine the
44   * DataBuffer location of each pixel.
45   *
46   * There is only one scanline stride for all bands.  The pixel stride
47   * is always equal to one.  This type of raster can be used with a
48   * ComponentColorModel. This class requires a BandedSampleModel.
49   *
50   */
51  public class ByteBandedRaster extends SunWritableRaster {
52  
53      /** Data offsets for each band of image data. */
54      int[]         dataOffsets;
55  
56      /** Scanline stride of the image data contained in this Raster. */
57      int           scanlineStride;
58  
59      /** The image data array. */
60      byte[][]      data;
61  
62      /** A cached copy of minX + width for use in bounds checks. */
63      private int maxX;
64  
65      /** A cached copy of minY + height for use in bounds checks. */
66      private int maxY;
67  
68      /**
69       *  Constructs a ByteBandedRaster with the given sampleModel. The
70       *  Raster's upper left corner is origin and it is the same
71       *  size as the SampleModel.  A dataBuffer large
72       *  enough to describe the Raster is automatically created. SampleModel
73       *  must be of type BandedSampleModel.
74       *  @param sampleModel     The SampleModel that specifies the layout.
75       *  @param origin          The Point that specifies the origin.
76       */
77      public ByteBandedRaster(SampleModel sampleModel,
78                                 Point origin) {
79          this(sampleModel,
80               sampleModel.createDataBuffer(),
81               new Rectangle(origin.x,
82                             origin.y,
83                             sampleModel.getWidth(),
84                             sampleModel.getHeight()),
85               origin,
86               null);
87      }
88  
89      /**
90       *  Constructs a ByteBanded Raster with the given sampleModel
91       *  and DataBuffer. The Raster's upper left corner is origin and
92       *  it is the same size as the SampleModel.  The DataBuffer is not
93       *  initialized and must be a DataBufferShort compatible with SampleModel.
94       *  SampleModel must be of type BandedSampleModel.
95       *  @param sampleModel     The SampleModel that specifies the layout.
96       *  @param dataBuffer      The DataBufferShort that contains the image data.
97       *  @param origin          The Point that specifies the origin.
98       */
99      public ByteBandedRaster(SampleModel sampleModel,
100                                DataBuffer dataBuffer,
101                                Point origin) {
102         this(sampleModel, dataBuffer,
103              new Rectangle(origin.x , origin.y,
104                            sampleModel.getWidth(),
105                            sampleModel.getHeight()),
106              origin, null);
107     }
108 
109     /**
110      *  Constructs a ByteBandedRaster with the given sampleModel,
111      *  DataBuffer, and parent. DataBuffer must be a DataBufferShort and
112      *  SampleModel must be of type BandedSampleModel.
113      *  When translated into the base Raster's
114      *  coordinate system, aRegion must be contained by the base Raster.
115      *  Origin is the coordinate in the new Raster's coordinate system of
116      *  the origin of the base Raster.  (The base Raster is the Raster's
117      *  ancestor which has no parent.)
118      *
119      *  Note that this constructor should generally be called by other
120      *  constructors or create methods, it should not be used directly.
121      *  @param sampleModel     The SampleModel that specifies the layout.
122      *  @param dataBuffer      The DataBufferShort that contains the image data.
123      *  @param aRegion         The Rectangle that specifies the image area.
124      *  @param origin          The Point that specifies the origin.
125      *  @param parent          The parent (if any) of this raster.
126      */
127     public ByteBandedRaster(SampleModel sampleModel,
128                             DataBuffer dataBuffer,
129                             Rectangle aRegion,
130                             Point origin,
131                             ByteBandedRaster parent) {
132 
133         super(sampleModel, dataBuffer, aRegion, origin, parent);
134         this.maxX = minX + width;
135         this.maxY = minY + height;
136 
137         if (!(dataBuffer instanceof DataBufferByte)) {
138            throw new RasterFormatException("ByteBandedRaster must have" +
139                 "byte DataBuffers");
140         }
141         DataBufferByte dbb = (DataBufferByte)dataBuffer;
142 
143         if (sampleModel instanceof BandedSampleModel) {
144             BandedSampleModel bsm = (BandedSampleModel)sampleModel;
145             this.scanlineStride = bsm.getScanlineStride();
146             int bankIndices[] = bsm.getBankIndices();
147             int bandOffsets[] = bsm.getBandOffsets();
148             int dOffsets[] = dbb.getOffsets();
149             dataOffsets = new int[bankIndices.length];
150             data = new byte[bankIndices.length][];
151             int xOffset = aRegion.x - origin.x;
152             int yOffset = aRegion.y - origin.y;
153             for (int i = 0; i < bankIndices.length; i++) {
154                data[i] = stealData(dbb, bankIndices[i]);
155                dataOffsets[i] = dOffsets[bankIndices[i]] +
156                    xOffset + yOffset*scanlineStride + bandOffsets[i];
157             }
158         } else {
159             throw new RasterFormatException("ByteBandedRasters must have"+
160                 "BandedSampleModels");
161         }
162         verify(false);
163     }
164 
165 
166     /**
167      * Returns a copy of the data offsets array. For each band the data
168      * offset is the index into the band's data array, of the first sample
169      * of the band.
170      */
171     public int[] getDataOffsets() {
172         return (int[])dataOffsets.clone();
173     }
174 
175     /**
176      * Returns data offset for the specified band.  The data offset
177      * is the index into the band's data array
178      * in which the first sample of the first scanline is stored.
179      * @param The band whose offset is returned.
180      */
181     public int getDataOffset(int band) {
182         return dataOffsets[band];
183     }
184 
185     /**
186      * Returns the scanline stride -- the number of data array elements
187      * between a given sample and the sample in the same column
188      * of the next row in the same band.
189      */
190     public int getScanlineStride() {
191         return scanlineStride;
192     }
193 
194     /**
195      * Returns the pixel stride, which is always equal to one for
196      * a Raster with a BandedSampleModel.
197      */
198     public int getPixelStride() {
199         return 1;
200     }
201 
202     /**
203      * Returns a reference to the entire data array.
204      */
205     public byte[][] getDataStorage() {
206         return data;
207     }
208 
209     /**
210      * Returns a reference to the specific band data array.
211      */
212     public byte[] getDataStorage(int band) {
213         return data[band];
214     }
215 
216     /**
217      * Returns the data elements for all bands at the specified
218      * location.
219      * An ArrayIndexOutOfBounds exception will be thrown at runtime
220      * if the pixel coordinate is out of bounds.
221      * A ClassCastException will be thrown if the input object is non null
222      * and references anything other than an array of transferType.
223      * @param x        The X coordinate of the pixel location.
224      * @param y        The Y coordinate of the pixel location.
225      * @param outData  An object reference to an array of type defined by
226      *                 getTransferType() and length getNumDataElements().
227      *                 If null an array of appropriate type and size will be
228      *                 allocated.
229      * @return         An object reference to an array of type defined by
230      *                 getTransferType() with the request pixel data.
231      */
232     public Object getDataElements(int x, int y, Object obj) {
233         if ((x < this.minX) || (y < this.minY) ||
234             (x >= this.maxX) || (y >= this.maxY)) {
235             throw new ArrayIndexOutOfBoundsException
236                 ("Coordinate out of bounds!");
237         }
238         byte outData[];
239         if (obj == null) {
240             outData = new byte[numDataElements];
241         } else {
242             outData = (byte[])obj;
243         }
244         int off = (y-minY)*scanlineStride + (x-minX);
245 
246         for (int band = 0; band < numDataElements; band++) {
247             outData[band] = data[band][dataOffsets[band] + off];
248         }
249 
250         return outData;
251     }
252 
253     /**
254      * Returns an  array  of data elements from the specified
255      * rectangular region.
256      * An ArrayIndexOutOfBounds exception will be thrown at runtime
257      * if the pixel coordinates are out of bounds.
258      * A ClassCastException will be thrown if the input object is non null
259      * and references anything other than an array of transferType.
260      * <pre>
261      *       byte[] bandData = (byte[])raster.getDataElement(x, y, w, h, null);
262      *       int numDataElements = raster.getNumDataElements();
263      *       byte[] pixel = new byte[numDataElements];
264      *       // To find a data element at location (x2, y2)
265      *       System.arraycopy(bandData, ((y2-y)*w + (x2-x))*numDataElements,
266      *                        pixel, 0, numDataElements);
267      * </pre>
268      * @param x        The X coordinate of the upper left pixel location.
269      * @param y        The Y coordinate of the upper left pixel location.
270      * @param width    Width of the pixel rectangle.
271      * @param height   Height of the pixel rectangle.
272      * @param outData  An object reference to an array of type defined by
273      *                 getTransferType() and length w*h*getNumDataElements().
274      *                 If null an array of appropriate type and size will be
275      *                 allocated.
276      * @return         An object reference to an array of type defined by
277      *                 getTransferType() with the request pixel data.
278      */
279     public Object getDataElements(int x, int y, int w, int h, Object obj) {
280         if ((x < this.minX) || (y < this.minY) ||
281             (x + w > this.maxX) || (y + h > this.maxY)) {
282             throw new ArrayIndexOutOfBoundsException
283                 ("Coordinate out of bounds!");
284         }
285         byte outData[];
286         if (obj == null) {
287             outData = new byte[numDataElements*w*h];
288         } else {
289             outData = (byte[])obj;
290         }
291         int yoff = (y-minY)*scanlineStride + (x-minX);
292 
293         for (int c = 0; c < numDataElements; c++) {
294             int off = c;
295             byte[] bank = data[c];
296             int dataOffset = dataOffsets[c];
297 
298             int yoff2 = yoff;
299             for (int ystart=0; ystart < h; ystart++, yoff2 += scanlineStride) {
300                 int xoff = dataOffset + yoff2;
301                 for (int xstart=0; xstart < w; xstart++) {
302                     outData[off] = bank[xoff++];
303                     off += numDataElements;
304                 }
305             }
306         }
307 
308         return outData;
309     }
310 
311     /**
312      * Returns a byte array  of data elements from the specified rectangular
313      * region for the specified band.
314      * An ArrayIndexOutOfBounds exception will be thrown at runtime
315      * if the pixel coordinates are out of bounds.
316      * <pre>
317      *       byte[] bandData = raster.getByteData(x, y, w, h, null);
318      *       // To find the data element at location (x2, y2)
319      *       byte bandElement = bandData[((y2-y)*w + (x2-x))];
320      * </pre>
321      * @param x        The X coordinate of the upper left pixel location.
322      * @param y        The Y coordinate of the upper left pixel location.
323      * @param width    Width of the pixel rectangle.
324      * @param height   Height of the pixel rectangle.
325      * @param band     The band to return.
326      * @param outData  If non-null, data elements for all bands
327      *                 at the specified location are returned in this array.
328      * @return         Data array with data elements for all bands.
329      */
330     public byte[] getByteData(int x, int y, int w, int h,
331                               int band, byte[] outData) {
332         // Bounds check for 'band' will be performed automatically
333         if ((x < this.minX) || (y < this.minY) ||
334             (x + w > this.maxX) || (y + h > this.maxY)) {
335             throw new ArrayIndexOutOfBoundsException
336                 ("Coordinate out of bounds!");
337         }
338         if (outData == null) {
339             outData = new byte[scanlineStride*h];
340         }
341         int yoff = (y-minY)*scanlineStride + (x-minX) + dataOffsets[band];
342 
343         if (scanlineStride == w) {
344             System.arraycopy(data[band], yoff, outData, 0, w*h);
345         } else {
346             int off = 0;
347             for (int ystart=0; ystart < h; ystart++, yoff += scanlineStride) {
348                 System.arraycopy(data[band], yoff, outData, off, w);
349                 off += w;
350             }
351         }
352 
353         return outData;
354     }
355 
356     /**
357      * Returns a byte array of data elements from the specified rectangular
358      * region.
359      * An ArrayIndexOutOfBounds exception will be thrown at runtime
360      * if the pixel coordinates are out of bounds.
361      * <pre>
362      *       byte[] bandData = raster.getByteData(x, y, w, h, null);
363      *       int numDataElements = raster.getNumDataElements();
364      *       byte[] pixel = new byte[numDataElements];
365      *       // To find a data element at location (x2, y2)
366      *       System.arraycopy(bandData, ((y2-y)*w + (x2-x))*numDataElements,
367      *                        pixel, 0, numDataElements);
368      * </pre>
369      * @param x        The X coordinate of the upper left pixel location.
370      * @param y        The Y coordinate of the upper left pixel location.
371      * @param width    Width of the pixel rectangle.
372      * @param height   Height of the pixel rectangle.
373      * @param outData  If non-null, data elements for all bands
374      *                 at the specified location are returned in this array.
375      * @return         Data array with data elements for all bands.
376      */
377     public byte[] getByteData(int x, int y, int w, int h, byte[] outData) {
378         if ((x < this.minX) || (y < this.minY) ||
379             (x + w > this.maxX) || (y + h > this.maxY)) {
380             throw new ArrayIndexOutOfBoundsException
381                 ("Coordinate out of bounds!");
382         }
383         if (outData == null) {
384             outData = new byte[numDataElements*scanlineStride*h];
385         }
386         int yoff = (y-minY)*scanlineStride + (x-minX);
387 
388         for (int c = 0; c < numDataElements; c++) {
389             int off = c;
390             byte[] bank = data[c];
391             int dataOffset = dataOffsets[c];
392 
393             // REMIND: Should keep track if dataoffsets are in a nice order
394             int yoff2 = yoff;
395             for (int ystart=0; ystart < h; ystart++, yoff2 += scanlineStride) {
396                 int xoff = dataOffset + yoff2;
397                 for (int xstart=0; xstart < w; xstart++) {
398                     outData[off] = bank[xoff++];
399                     off += numDataElements;
400                 }
401             }
402         }
403 
404         return outData;
405     }
406 
407     /**
408      * Stores the data elements for all bands at the specified location.
409      * An ArrayIndexOutOfBounds exception will be thrown at runtime
410      * if the pixel coordinate is out of bounds.
411      * A ClassCastException will be thrown if the input object is non null
412      * and references anything other than an array of transferType.
413      * @param x        The X coordinate of the pixel location.
414      * @param y        The Y coordinate of the pixel location.
415      * @param inData   An object reference to an array of type defined by
416      *                 getTransferType() and length getNumDataElements()
417      *                 containing the pixel data to place at x,y.
418      */
419     public void setDataElements(int x, int y, Object obj) {
420         if ((x < this.minX) || (y < this.minY) ||
421             (x >= this.maxX) || (y >= this.maxY)) {
422             throw new ArrayIndexOutOfBoundsException
423                 ("Coordinate out of bounds!");
424         }
425         byte inData[] = (byte[])obj;
426         int off = (y-minY)*scanlineStride + (x-minX);
427         for (int i = 0; i < numDataElements; i++) {
428             data[i][dataOffsets[i] + off] = inData[i];
429         }
430         markDirty();
431     }
432 
433     /**
434      * Stores the Raster data at the specified location.
435      * An ArrayIndexOutOfBounds exception will be thrown at runtime
436      * if the pixel coordinate is out of bounds.
437      * @param x          The X coordinate of the pixel location.
438      * @param y          The Y coordinate of the pixel location.
439      * @param inRaster   Raster of data to place at x,y location.
440      */
441     public void setDataElements(int x, int y, Raster inRaster) {
442         int dstOffX = inRaster.getMinX() + x;
443         int dstOffY = inRaster.getMinY() + y;
444         int width  = inRaster.getWidth();
445         int height = inRaster.getHeight();
446         if ((dstOffX < this.minX) || (dstOffY < this.minY) ||
447             (dstOffX + width > this.maxX) || (dstOffY + height > this.maxY)) {
448             throw new ArrayIndexOutOfBoundsException
449                 ("Coordinate out of bounds!");
450         }
451 
452         setDataElements(dstOffX, dstOffY, width, height, inRaster);
453     }
454 
455    /**
456      * Stores the Raster data at the specified location.
457      * @param dstX The absolute X coordinate of the destination pixel
458      * that will receive a copy of the upper-left pixel of the
459      * inRaster
460      * @param dstY The absolute Y coordinate of the destination pixel
461      * that will receive a copy of the upper-left pixel of the
462      * inRaster
463      * @param width      The number of pixels to store horizontally
464      * @param height     The number of pixels to store vertically
465      * @param inRaster   Raster of data to place at x,y location.
466      */
467     private void setDataElements(int dstX, int dstY,
468                                  int width, int height,
469                                  Raster inRaster) {
470         // Assume bounds checking has been performed previously
471         if (width <= 0 || height <= 0) {
472             return;
473         }
474 
475         int srcOffX = inRaster.getMinX();
476         int srcOffY = inRaster.getMinY();
477         Object tdata = null;
478 
479 //      // REMIND: Do something faster!
480 //      if (inRaster instanceof ByteBandedRaster) {
481 //      }
482 
483         for (int startY=0; startY < height; startY++) {
484             // Grab one scanline at a time
485             tdata = inRaster.getDataElements(srcOffX, srcOffY+startY,
486                                              width, 1, tdata);
487             setDataElements(dstX, dstY+startY, width, 1, tdata);
488         }
489     }
490 
491     /**
492      * Stores an array of data elements into the specified rectangular
493      * region.
494      * An ArrayIndexOutOfBounds exception will be thrown at runtime
495      * if the pixel coordinates are out of bounds.
496      * A ClassCastException will be thrown if the input object is non null
497      * and references anything other than an array of transferType.
498      * The data elements in the
499      * data array are assumed to be packed.  That is, a data element
500      * for the nth band at location (x2, y2) would be found at:
501      * <pre>
502      *      inData[((y2-y)*w + (x2-x))*numDataElements + n]
503      * </pre>
504      * @param x        The X coordinate of the upper left pixel location.
505      * @param y        The Y coordinate of the upper left pixel location.
506      * @param w        Width of the pixel rectangle.
507      * @param h        Height of the pixel rectangle.
508      * @param inData   An object reference to an array of type defined by
509      *                 getTransferType() and length w*h*getNumDataElements()
510      *                 containing the pixel data to place between x,y and
511      *                 x+h, y+h.
512      */
513     public void setDataElements(int x, int y, int w, int h, Object obj) {
514         if ((x < this.minX) || (y < this.minY) ||
515             (x + w > this.maxX) || (y + h > this.maxY)) {
516             throw new ArrayIndexOutOfBoundsException
517                 ("Coordinate out of bounds!");
518         }
519         byte inData[] = (byte[])obj;
520         int yoff = (y-minY)*scanlineStride + (x-minX);
521 
522         for (int c = 0; c < numDataElements; c++) {
523             int off = c;
524             byte[] bank = data[c];
525             int dataOffset = dataOffsets[c];
526 
527             int yoff2 = yoff;
528             for (int ystart=0; ystart < h; ystart++, yoff2 += scanlineStride) {
529                 int xoff = dataOffset + yoff2;
530                 for (int xstart=0; xstart < w; xstart++) {
531                     bank[xoff++] = inData[off];
532                     off += numDataElements;
533                 }
534             }
535         }
536 
537         markDirty();
538     }
539 
540     /**
541      * Stores a byte array of data elements into the specified rectangular
542      * region.
543      * An ArrayIndexOutOfBounds exception will be thrown at runtime
544      * if the pixel coordinates are out of bounds.
545      * The data elements in the
546      * data array are assumed to be packed.  That is, a data element
547      * for the nth band at location (x2, y2) would be found at:
548      * <pre>
549      *      inData[((y2-y)*w + (x2-x))*numDataElements + n]
550      * </pre>
551      * @param x        The X coordinate of the upper left pixel location.
552      * @param y        The Y coordinate of the upper left pixel location.
553      * @param w        Width of the pixel rectangle.
554      * @param h        Height of the pixel rectangle.
555      * @param band     The band to set.
556      * @param inData   The data elements to be stored.
557      */
558     public void putByteData(int x, int y, int w, int h,
559                             int band, byte[] inData) {
560         // Bounds check for 'band' will be performed automatically
561         if ((x < this.minX) || (y < this.minY) ||
562             (x + w > this.maxX) || (y + h > this.maxY)) {
563             throw new ArrayIndexOutOfBoundsException
564                 ("Coordinate out of bounds!");
565         }
566         int yoff = (y-minY)*scanlineStride + (x-minX) + dataOffsets[band];
567         int xoff;
568         int off = 0;
569         int xstart;
570         int ystart;
571 
572         if (scanlineStride == w) {
573             System.arraycopy(inData, 0, data[band], yoff, w*h);
574         } else {
575             for (ystart=0; ystart < h; ystart++, yoff += scanlineStride) {
576                 System.arraycopy(inData, off, data[band], yoff, w);
577                 off += w;
578             }
579         }
580 
581         markDirty();
582     }
583 
584     /**
585      * Stores a byte array of data elements into the specified rectangular
586      * region.
587      * An ArrayIndexOutOfBounds exception will be thrown at runtime
588      * if the pixel coordinates are out of bounds.
589      * The data elements in the
590      * data array are assumed to be packed.  That is, a data element
591      * for the nth band at location (x2, y2) would be found at:
592      * <pre>
593      *      inData[((y2-y)*w + (x2-x))*numDataElements + n]
594      * </pre>
595      * @param x        The X coordinate of the upper left pixel location.
596      * @param y        The Y coordinate of the upper left pixel location.
597      * @param w        Width of the pixel rectangle.
598      * @param h        Height of the pixel rectangle.
599      * @param inData   The data elements to be stored.
600      */
601     public void putByteData(int x, int y, int w, int h, byte[] inData) {
602         if ((x < this.minX) || (y < this.minY) ||
603             (x + w > this.maxX) || (y + h > this.maxY)) {
604             throw new ArrayIndexOutOfBoundsException
605                 ("Coordinate out of bounds!");
606         }
607         int yoff = (y-minY)*scanlineStride + (x-minX);
608 
609         for (int c = 0; c < numDataElements; c++) {
610             int off = c;
611             byte[] bank = data[c];
612             int dataOffset = dataOffsets[c];
613 
614             int yoff2 = yoff;
615             for (int ystart=0; ystart < h; ystart++, yoff2 += scanlineStride) {
616                 int xoff = dataOffset + yoff2;
617                 for (int xstart=0; xstart < w; xstart++) {
618                     bank[xoff++] = inData[off];
619                     off += numDataElements;
620                 }
621             }
622         }
623 
624         markDirty();
625     }
626 
627     /**
628      * Creates a Writable subraster given a region of the raster.  The x and y
629      * coordinates specify the horizontal and vertical offsets
630      * from the upper-left corner of this raster to the upper-left corner
631      * of the subraster.  A subset of the bands of the parent Raster may
632      * be specified.  If this is null, then all the bands are present in the
633      * subRaster. A translation to the subRaster may also be specified.
634      * Note that the subraster will reference the same
635      * DataBuffers as the parent raster, but using different offsets.
636      * @param x               X offset.
637      * @param y               Y offset.
638      * @param width           Width of the subraster.
639      * @param height          Height of the subraster.
640      * @param x0              Translated X origin of the subraster.
641      * @param y0              Translated Y origin of the subraster.
642      * @param bandList        Array of band indices.
643      * @exception RasterFormatException
644      *            if the specified bounding box is outside of the parent raster.
645      */
646     public WritableRaster createWritableChild (int x, int y,
647                                                int width, int height,
648                                                int x0, int y0,
649                                                int bandList[]) {
650 
651         if (x < this.minX) {
652             throw new RasterFormatException("x lies outside raster");
653         }
654         if (y < this.minY) {
655             throw new RasterFormatException("y lies outside raster");
656         }
657         if ((x+width < x) || (x+width > this.width + this.minX)) {
658             throw new RasterFormatException("(x + width) is outside raster") ;
659         }
660         if ((y+height < y) || (y+height > this.height + this.minY)) {
661             throw new RasterFormatException("(y + height) is outside raster");
662         }
663 
664         SampleModel sm;
665 
666         if (bandList != null)
667             sm = sampleModel.createSubsetSampleModel(bandList);
668         else
669             sm = sampleModel;
670 
671         int deltaX = x0 - x;
672         int deltaY = y0 - y;
673 
674         return new ByteBandedRaster(sm,
675                                     dataBuffer,
676                                     new Rectangle(x0,y0,width,height),
677                                     new Point(sampleModelTranslateX+deltaX,
678                                               sampleModelTranslateY+deltaY),
679                                     this);
680     }
681 
682     /**
683      * Creates a subraster given a region of the raster.  The x and y
684      * coordinates specify the horizontal and vertical offsets
685      * from the upper-left corner of this raster to the upper-left corner
686      * of the subraster.  A subset of the bands of the parent Raster may
687      * be specified.  If this is null, then all the bands are present in the
688      * subRaster. A translation to the subRaster may also be specified.
689      * Note that the subraster will reference the same
690      * DataBuffers as the parent raster, but using different offsets.
691      * @param x               X offset.
692      * @param y               Y offset.
693      * @param width           Width (in pixels) of the subraster.
694      * @param height          Height (in pixels) of the subraster.
695      * @param x0              Translated X origin of the subraster.
696      * @param y0              Translated Y origin of the subraster.
697      * @param bandList        Array of band indices.
698      * @exception RasterFormatException
699      *            if the specified bounding box is outside of the parent raster.
700      */
701     public Raster createChild (int x, int y,
702                                    int width, int height,
703                                    int x0, int y0,
704                                    int bandList[]) {
705         return createWritableChild(x, y, width, height, x0, y0, bandList);
706     }
707 
708     /**
709      * Creates a Raster with the same layout but using a different
710      * width and height, and with new zeroed data arrays.
711      */
712     public WritableRaster createCompatibleWritableRaster(int w, int h) {
713         if (w <= 0 || h <=0) {
714             throw new RasterFormatException("negative "+
715                                           ((w <= 0) ? "width" : "height"));
716         }
717 
718         SampleModel sm = sampleModel.createCompatibleSampleModel(w,h);
719 
720         return new ByteBandedRaster(sm, new Point(0,0));
721     }
722 
723     /**
724      * Creates a Raster with the same layout and the same
725      * width and height, and with new zeroed data arrays.  If
726      * the Raster is a subRaster, this will call
727      * createCompatibleRaster(width, height).
728      */
729     public WritableRaster createCompatibleWritableRaster() {
730         return createCompatibleWritableRaster(width, height);
731     }
732 
733     /**
734      * Verify that the layout parameters are consistent with
735      * the data.  If strictCheck
736      * is false, this method will check for ArrayIndexOutOfBounds conditions.  If
737      * strictCheck is true, this method will check for additional error
738      * conditions such as line wraparound (width of a line greater than
739      * the scanline stride).
740      * @return   String   Error string, if the layout is incompatible with
741      *                    the data.  Otherwise returns null.
742      */
743     private void verify (boolean strictCheck) {
744         // Make sure data for Raster is in a legal range
745         for (int i=0; i < dataOffsets.length; i++) {
746             if (dataOffsets[i] < 0) {
747                 throw new RasterFormatException("Data offsets for band "+i+
748                                                 "("+dataOffsets[i]+
749                                                 ") must be >= 0");
750             }
751         }
752 
753         int maxSize = 0;
754         int size;
755 
756         for (int i=0; i < numDataElements; i++) {
757             size = (height-1)*scanlineStride + (width-1) + dataOffsets[i];
758             if (size > maxSize) {
759                 maxSize = size;
760             }
761         }
762 
763         if (data.length == 1) {
764             if (data[0].length < maxSize*numDataElements) {
765                 throw new RasterFormatException("Data array too small "+
766                                                 "(it is "+data[0].length+
767                                                 " and should be "+
768                                                 (maxSize*numDataElements)+
769                                                 " )");
770             }
771         }
772         else {
773             for (int i=0; i < numDataElements; i++) {
774                 if (data[i].length < maxSize) {
775                     throw new RasterFormatException("Data array too small "+
776                                                     "(it is "+data[i].length+
777                                                     " and should be "+
778                                                     maxSize+" )");
779                 }
780             }
781         }
782     }
783 
784     public String toString() {
785         return new String ("ByteBandedRaster: width = "+width+" height = "
786                            + height
787                            +" #bands "+numDataElements
788                            +" minX = "+minX+" minY = "+minY);
789     }
790 
791 }